package com.unbiz.common.mutable;

import java.lang.ref.WeakReference;

import javafx.beans.InvalidationListener;
import javafx.beans.Observable;
import javafx.beans.value.ChangeListener;
import javafx.beans.value.ObservableValue;

import com.sun.javafx.binding.ExpressionHelper;
import com.unbiz.common.ClassUtil;
import javafx.beans.value.WritableValue;

/**
 * 
 * @author <a href="mailto:xuchen06@baidu.com">xuc</a>
 * @version create on 2014年9月22日 下午4:28:37
 */
public abstract class MutableNumber<T> extends Number implements ObservableValue<T>, WritableValue<T>, Cloneable {

    /**
	 * 
	 */
    private static final long serialVersionUID = -6971948552591529691L;
    

    private ExpressionHelper<T> helper;
    private boolean valid = true;

    /**
     * {@inheritDoc}
     */
    @Override
    public void addListener(InvalidationListener listener) {
        helper = ExpressionHelper.addListener(helper, this, listener);
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public void addListener(ChangeListener<? super T> listener) {
        helper = ExpressionHelper.addListener(helper, this, listener);
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public void removeListener(InvalidationListener listener) {
        helper = ExpressionHelper.removeListener(helper, listener);
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public void removeListener(ChangeListener<? super T> listener) {
        helper = ExpressionHelper.removeListener(helper, listener);
    }

    /**
     * Notify the currently registered observers of a value change.
     * 
     * This implementation will ignore all adds and removes of observers that
     * are done while a notification is processed. The changes take effect in
     * the following call to fireValueChangedEvent.
     */
    protected void fireValueChangedEvent() {
        ExpressionHelper.fireValueChangedEvent(helper);
    }
    

    // ---------------------------------------------------------------- object
    
    /**
     * {@inheritDoc}
     */ 
    public T get() {
        valid = true;
       // return observable == null ? value : observable.get();
        return getValue();
    }

    /**
     * {@inheritDoc}
     */  
    public void set(T newValue) {      
    	T value = getValue();
        if (!value.equals(newValue)) {
            setValue(newValue);
            markInvalid();  
        }
    }

    protected void markInvalid() {
        if (valid) {
            valid = false;
            invalidated();
            fireValueChangedEvent();
        }
    }


    /**
     * The method {@code invalidated()} can be overridden to receive
     * invalidation notifications. This is the preferred option in
     * {@code Objects} defining the property, because it requires less memory.
     *
     * The default implementation is empty.
     */
    protected void invalidated() {
    }
    
    static class Listener implements InvalidationListener {

        private final WeakReference<MutableNumber> wref;

        public Listener(MutableNumber ref) {
            this.wref = new WeakReference<>(ref);
        }

        @Override
        public void invalidated(Observable observable) {
        	MutableNumber ref = wref.get();
            if (ref == null) {
                observable.removeListener(this);
            } else {
                ref.markInvalid();
            }
        }
    }
}